import { exec } from "child_process";
import Fs from "fs-extra";
import klawSync from "klaw-sync";
import nodeXlsx from "node-xlsx";
import { extname, relative, resolve } from "path";
import { Workbook } from "xlsx-rw";
import type {
  Sheet2JSONOpts,
  ParsingOptions,
} from "node-xlsx/node_modules/xlsx/types";

import {
  objArrToCsv,
  ObjArrToCsvParseFunc,
  csvToObjArr1,
  CsvToObjArrOptions,
} from "../common/index.js";

/**
 * 当albert.gao作为node_module时，工程文件根目录(node_modules的上一级文件夹)
 */
export const __projectDir: string = resolve(__dirname, "../../../");
/**
 * 当albert.gao作为node_module时，工程文件根目录(node_modules的上一级文件夹)
 */
export const PROJECT_DIR = __projectDir;

/**
 * 强制关闭Excel应用
 */
export function killExcel(): void {
  exec("taskkill /f /im et.exe");
  exec("taskkill /f /im excel.exe");
}

/**
 * 自动将数据写入excel
 * @param xlsx文件的路径
 * @param data *
 * @param {number}[titlesFrom=0] 从哪个index读取titles
 * @param {Function}[parser = (value, key, i) => value || ''] 传入(值 ,键, 行index)，返回该位置的值
 */
export function writeABook(
  path: string,
  data: Object[] | any[][],
  titlesFrom = 0,
  parser: ObjArrToCsvParseFunc = (value) => value || ""
): void {
  try {
    Fs.removeSync(path);
  } finally {
    //
  }
  Fs.ensureFileSync(path);
  const wb1 = new Workbook(path);
  if (
    data[0] &&
    /object\s+(object)/i.test(Object.prototype.toString.call(data[0]))
  ) {
    // @ts-ignore 如果是any[][]
    wb1.writeData(wb1.SheetNames[0], data);
  } else {
    wb1.writeData(wb1.SheetNames[0], objArrToCsv(data, titlesFrom, parser));
  }
  wb1.save(path);
}

export interface ReadAXlsxOptions {
  /**
   * xlsx文件的绝对路径
   */
  path: string;
  /**
   * csv转为Object[]时的解析参数, 传给csvToObjArr1
   */
  csvToObjOptions?: Partial<CsvToObjArrOptions>;
  /**
   * 读取xlsx文件时的解析参数, 传给node-xlsx
   */
  parseOptions?: Sheet2JSONOpts & ParsingOptions;
}
export function readAXlsx<Row extends Object = Object>({
  path,
  csvToObjOptions = {},
  parseOptions,
}: ReadAXlsxOptions) {
  const data = nodeXlsx.parse(path, parseOptions)[0].data as any[][];
  const final = csvToObjArr1({
    ...csvToObjOptions,
    csv: data,
  }) as Row[];
  return final;
}

/**
 * require某个js
 * @param path js文件的完整路径
 * @returns 返回require该js文件
 */
export function getModule(path: string): any {
  let relPath = relative(__dirname, path);
  relPath = relPath.replace(/\\/g, "/");
  if (!/^\.\//.test(relPath)) {
    relPath = "./" + relPath;
  }
  return require(relPath);
}

/**
 * require某个文件夹下的所有js, 如果该模块导出的内容有.priority，则该数值越大，越优先导入，否则按照文件名顺序导入
 * @param directory 文件夹完整路径路径
 * @param filter 过滤器, 返回true则加入
 * @returns 该文件夹下所有require的js
 */
export function getModules(
  directory: string,
  filter: (
    /** js文件的完整路径 */
    path: string
  ) => boolean = () => true
): {
  /**
   * 该模块的绝对路径
   */
  path: string;
  /**
   * require(该模块)
   */
  module: any;
}[] {
  return klawSync(directory, { nodir: true })
    .filter((item) => /\.js$/i.test(extname(item.path)) && filter(item.path))
    .map(({ path }) => {
      return { path, module: getModule(path) };
    })
    .sort((a, b) => {
      const _compare = (b.module.priority || 0) - (a.module.priority || 0);
      if (_compare) {
        return _compare;
      } else {
        return a.path.localeCompare(b.path);
      }
    });
}

/**
 * 获取文件大小
 * @param path 文件完整路径
 * @returns **MB **GB 或 **KB或**b格式
 */
export function getFileSize(path: string, digits: number = 1): string {
  let size: number | string = Fs.statSync(path).size;
  if (size > 1024 ** 3) {
    size = `${~~((size * 10 ** digits) / 1024 ** 3) / 10 ** digits}GB`;
  } else if (size > 1024 ** 2) {
    size = `${~~((size * 10 ** digits) / 1024 ** 2) / 10 ** digits}MB`;
  } else if (size > 1024) {
    size = `${~~((size * 10 ** digits) / 1024) / 10 ** digits}KB`;
  } else {
    size = `${size}b`;
  }
  return size;
}
